#include <CGAL/Polyhedron_incremental_builder_3.h>
#include "TriangleMesh\structure\Enriched_polyhedron.h"

#define MAX_LINE_VRML 10000
#define MAX_WORD_VRML 1000

template <class HDS>
class Builder_wrl : public CGAL::Modifier_base<HDS>
{
private:
	typedef typename HDS::Vertex::Point Point;
	typedef typename CGAL::Polyhedron_incremental_builder_3<HDS> Builder;

	CString	m_sFileName;
	int		m_SizeFile;
	char	*m_pBuffer;
	int		m_IndexBuffer;
	char	m_pBufferLine[MAX_LINE_VRML];
	char	m_pBufferWord[MAX_WORD_VRML];

public:
	Builder_wrl(CString sFileName)
	{
		m_sFileName = sFileName;
		m_pBuffer = NULL;
		m_pBufferWord[0] = '\0';
		m_pBufferLine[0] = '\0';
		m_IndexBuffer = 0;
	}
	~Builder_wrl() 
	{
		free_buffer();
	}

	void operator()(HDS& hds)
	{
		// Free buffer
		free_buffer();
		// Read file
		read_file();
		// Check version
		if (!check_version()) {
			AfxMessageBox("WRL Parsing: not version2.0");
			return;
		}
		// Count components
	//	count_def();
	//	count_mesh();
		// Read mesh
		Builder builder(hds, true);
		builder.begin_surface(3, 1, 6);
		// For different header
		do {
			if (!offset_to_string("Shape"))
			{
				break;
			}
			store_mesh(builder);
		} while (1);

		builder.end_surface();
	}

private:
	void free_buffer (void)
	{
		if(m_pBuffer != NULL)
			delete [] m_pBuffer;
		m_IndexBuffer = 0;
	}

	int read_file(void)
	{
		CFile file;
		CFileException ex;
		// Opening
		if(!file.Open(m_sFileName, CFile::modeRead|CFile::typeBinary,&ex))
		{
			CString s;
			s.Format("File could not be opened %d", ex.m_cause);
			AfxMessageBox(s);
			return 0;
		}
		// Size file
		m_SizeFile = file.GetLength();
		// Alloc
		m_pBuffer = new char[m_SizeFile];
		if(m_pBuffer == NULL)
		{
			AfxMessageBox("insuffisant memory");
			return 0;
		}
		// Reading
		TRY
		{
			file.Read(m_pBuffer, m_SizeFile);
		}
		CATCH (CFileException, e)
		{
			file.Close();
			CString s;
			s.Format("Error during reading %d", e->m_cause);
			AfxMessageBox(s);
			return 0;
		}
		END_CATCH
		// Closing
		file.Close();
		return 1;
	}

	int check_version()
	{
		read_line();
		if (strstr(m_pBufferLine,"#VRML V2.0") != NULL)
		{
			return 1;
		}
		return 0;
	}

	int count_mesh(void)
	{
		int tmp = m_IndexBuffer;
		int nb = 0;
		while (m_IndexBuffer < m_SizeFile)
		{
			read_line();
			if (strstr(m_pBufferLine,"IndexedFaceSet") != NULL) {
				nb++;
			}
		}
		m_IndexBuffer = tmp;
		return nb;
	}

	int count_def(void)
	{
		int tmp = m_IndexBuffer;
		int nb = 0;
		while (m_IndexBuffer < m_SizeFile)
		{
			read_line();
			if (strncmp(m_pBufferLine,"DEF",3) == 0) {
				nb++;
			}
		}
		m_IndexBuffer = tmp;
		return nb;
	}

	int read_word(void)
	{
		m_pBufferWord[0] = '\0';
		int i=0;

		// Jump to next valid character
		while((m_pBuffer[m_IndexBuffer] == '\n' || 
				m_pBuffer[m_IndexBuffer] == '\t' || 
				m_pBuffer[m_IndexBuffer] == '\r' || 
				m_pBuffer[m_IndexBuffer] == ' ' ||
				m_pBuffer[m_IndexBuffer] == ',') &&
				m_IndexBuffer < m_SizeFile) 
		{
			m_IndexBuffer++;
		}
		// Check eof
		if (m_IndexBuffer >= m_SizeFile)
		{
			return 0;
		}
		do {
			m_pBufferWord[i++] = m_pBuffer[m_IndexBuffer++];
		} while(m_pBuffer[m_IndexBuffer-1] != '\n' && 
				m_pBuffer[m_IndexBuffer-1] != '\t' && 
				m_pBuffer[m_IndexBuffer-1] != '\r' && 
				m_pBuffer[m_IndexBuffer-1] != ' ' && 
				m_pBuffer[m_IndexBuffer-1] != ',' && 
				i < MAX_WORD_VRML &&
				m_IndexBuffer < m_SizeFile);

		m_pBufferWord[i-1] = '\0';
		return 1;
	}

	int read_line(void)
	{
		m_pBufferLine[0] = '\0';
		int i=0;
		do {
			m_pBufferLine[i++] = m_pBuffer[m_IndexBuffer++];
		} while(m_pBuffer[m_IndexBuffer-1]!='\n' && i<MAX_LINE_VRML && m_IndexBuffer<m_SizeFile);

		m_pBufferLine[i-1] = '\0';
		return 1;
	}

	int offset_to_string(char *string)
	{
		while (m_IndexBuffer < m_SizeFile)
		{
			read_line();
			char *adr = strstr(m_pBufferLine, string);
			if(strstr(m_pBufferLine, string) != NULL)
			{
				m_IndexBuffer = m_IndexBuffer - strlen(m_pBufferLine) - 1 + (adr-m_pBufferLine);
				char *pdest = strchr(m_pBufferLine, '#');
				if (pdest == NULL) {
					return 1;
				}
				if ((int)(pdest) > (int)(adr)) {
					return 1;
				}
				// skip this token since it is behind #
				m_IndexBuffer += strlen(string);
			}
		}
		return 0;
	}

	int offset_to_string_begin_line(char *string)
	{
		while (m_IndexBuffer < m_SizeFile)
		{
			read_line();
			if (strncmp(m_pBufferLine, string, strlen(string)) == 0)
			{
				m_IndexBuffer -= strlen(m_pBufferLine)+1;
				return 1;
			}
		}
		return 0;
	}

	int store_mesh(Builder &builder)
	{
		int tmp = m_IndexBuffer;

		if (!offset_to_string("IndexedFaceSet"))
		{
			AfxMessageBox("WRL_parser: IndexedFaceSet not found!");
			return 0;
		}

		// Store vertices
		//***********************************************Coordinate { 
		if (!offset_to_string("point"))
		{
			AfxMessageBox("WRL_parser: point not found!");
			return 0;
		}
		m_IndexBuffer += strlen("point");
		if (!offset_to_string("["))
		{
			AfxMessageBox("WRL_parser: [ not found!");
			return 0;
		}
		m_IndexBuffer += 1;
		// Cur : x y z,
		// End : x y z]
		int success;
		do {
			float x,y,z;
			read_word();
			success = sscanf(m_pBufferWord,"%f",&x);
			read_word();
			success &= sscanf(m_pBufferWord,"%f",&y);
			read_word();
			success &= sscanf(m_pBufferWord,"%f",&z);
			if (success)
				builder.add_vertex(Point(x,y,z));
		} while(success);		

		// Store faces, accept only triangles
		//***********************************************
		m_IndexBuffer = tmp;
		if (!offset_to_string("coordIndex"))
		{
			AfxMessageBox("WRL_parser: coordIndex not found!");
			return 0;
		}
		m_IndexBuffer += strlen("coordIndex");
		if (!offset_to_string("["))
		{
			AfxMessageBox("WRL_parser: [ not found!");
			return 0;
		}
		m_IndexBuffer += 1;

		// Cur : int, int, int, -1,
		// End : int, int, int, -1]
		do {
			int v1,v2,v3;
			read_word();
			success  = sscanf(m_pBufferWord,"%d,",&v1);
			read_word();
			success &= sscanf(m_pBufferWord,"%d,",&v2);
			read_word();
			success &= sscanf(m_pBufferWord,"%d,",&v3);

			ASSERT(v1 >= 0);
			ASSERT(v2 >= 0);
			ASSERT(v3 >= 0);

			if (success && v1 >= 0 && v2 >= 0 && v3 >= 0)
			{
				builder.begin_facet();
				builder.add_vertex_to_facet(v1);
				builder.add_vertex_to_facet(v2);
				builder.add_vertex_to_facet(v3);
				builder.end_facet();
			}

			int test;
			read_word();
			sscanf(m_pBufferWord, "%d", &test);
			if (strstr(m_pBufferWord,"]") != NULL) 
			{
				success = 0;
			}
		} while (success);

		// success
		return 1;
	}
};

template <class kernel, class items>
class Parser_wrl
{

public:
	typedef typename Enriched_polyhedron<kernel,items>::HalfedgeDS HalfedgeDS;
	Parser_wrl() {}
	~Parser_wrl() {}

public:
	bool read(const char *pFilename, Enriched_polyhedron<kernel,items> *pMesh)
	{
		CGAL_assertion(pMesh != NULL);
		Builder_wrl<HalfedgeDS> builder(pFilename);
		pMesh->delegate(builder);
		return true;
	}
};